﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net;
using log4net;

namespace Net
{
    public class Net
    {
        public Socket socket;

        private byte packageIdentifier;
        public byte PackageIdentifier
        {
            get { return packageIdentifier; }
        }

        private static ILog log = LogManager.GetLogger(typeof(Net).Name);
        

        public Net()
        {
            packageIdentifier = 0;
        }

        /// <summary>
        /// 与服务器端的连接状态
        /// </summary>
        /// <returns>如果连接正常则返回true，未连接则返回false</returns>
        public bool IsConnect()
        {
            if (socket == null) return false;
            if (!socket.Connected) return false;
            else return true;
        }

        /// <summary>
        /// 连接到服务器
        /// </summary>
        /// <param name="IP">远程服务器IP</param>
        /// <param name="port">远程服务器端口</param>
        /// <returns>如果连接成功则返回true</returns>
        public bool Connect(string ipAddress, int port)
        {
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            try
            {
                socket.Connect(ipAddress, port);
				ObjectState objectState = new ObjectState();
                socket.BeginReceive(objectState.buffer, 0, ObjectState.BufferLength, SocketFlags.None, new AsyncCallback(ReceiveCallback), objectState);
            }
            catch (Exception)
            {
                return false;
            }
            return true;
        }

        private void ReceiveCallback(IAsyncResult ar)
        {
			try
			{
				ObjectState objectState = (ObjectState)ar.AsyncState;
				int bytesRead = socket.EndReceive(ar);
				if(bytesRead > 0)
				{
					ProcessReceive(objectState, bytesRead);
				}
			}
			catch(Exception e)
			{
				return;
			}
        }

        private void ProcessReceive(ObjectState objectState, int bytesRead)
        {
            if (objectState.state == ObjectState.Begin)
            {
                if (bytesRead < 4)
                {
                    return;
                }
                objectState.packageLength = BitConverter.ToInt32(objectState.buffer, objectState.offset);
                if (objectState.packageLength < 9 )
                {
                    return;
                }
				if (objectState.packageLength > ObjectState.BufferLength)
				{
					ObjectState o = new ObjectState();
					o.buffer = new byte[objectState.packageLength];
					Array.Copy(objectState.buffer, objectState.offset, o.buffer, 0, bytesRead);
					o.packageLength = objectState.packageLength;
					o.offset = bytesRead;
					o.dataReceived = bytesRead;
					o.state = ObjectState.WaitNextPackagePart;
					socket.BeginReceive(o.buffer, 0, o.packageLength, SocketFlags.None, new AsyncCallback(ReceiveCallback), o);
					return;
				}

                if (bytesRead < objectState.packageLength)
                {
                    objectState.offset += bytesRead;
                    objectState.state = ObjectState.WaitNextPackagePart;
                    objectState.dataReceived = bytesRead;
                    socket.BeginReceive(objectState.buffer, objectState.offset, objectState.packageLength - objectState.dataReceived, SocketFlags.None, new AsyncCallback(ReceiveCallback), objectState);
                    return;
                }
                else if (bytesRead > objectState.packageLength)
                {
                    new PackageParser(objectState).ProcessPackage();
                    objectState.state = ObjectState.Begin;
                    objectState.offset += objectState.packageLength;
                    ProcessReceive(objectState, bytesRead - objectState.packageLength);
                    return;
                }
                else // bytesRead == packageLength
                {
                    new PackageParser(objectState).ProcessPackage();
                    objectState.offset = 0;
                    socket.BeginReceive(objectState.buffer, 0, ObjectState.BufferLength, SocketFlags.None, new AsyncCallback(ReceiveCallback), objectState);
                    return;
                }
            }
            else if (objectState.state == ObjectState.WaitNextPackagePart)
            {
                if (objectState.dataReceived + bytesRead < objectState.packageLength)
                {
                    objectState.offset += bytesRead;
                    objectState.dataReceived += bytesRead;
                    socket.BeginReceive(objectState.buffer, objectState.offset, objectState.packageLength - objectState.dataReceived, SocketFlags.None, new AsyncCallback(ReceiveCallback), objectState);
                    return;
                }
                else if (objectState.dataReceived + bytesRead == objectState.packageLength)
                {
					objectState.offset -= objectState.dataReceived;
                    new PackageParser(objectState).ProcessPackage();
                    ObjectState o = new ObjectState();
                    socket.BeginReceive(o.buffer, 0, ObjectState.BufferLength, SocketFlags.None, new AsyncCallback(ReceiveCallback), o);
                    return;
                }
            }
        }

        public void ProcessSend(string code, string request)
        {
            byte[] data = Encoding.Unicode.GetBytes(request);
            int packageLength = data.Length + 9;
            byte[] package = new byte[packageLength];
            Array.Copy(BitConverter.GetBytes(packageLength), 0, package, 0, 4);
            package[4] = packageIdentifier;
            Encoding.ASCII.GetBytes(code, 0, 4, package, 5);
            Array.Copy(data, 0, package, 9, data.Length);
            socket.Send(package);
            packageIdentifier++;
        }

        public void ProcessSend(string code, byte[] data)
        {
            int packageLength = data.Length + 9;
            byte[] package = new byte[packageLength];
            Array.Copy(BitConverter.GetBytes(packageLength), 0, package, 0, 4);
            package[4] = packageIdentifier;
            Encoding.ASCII.GetBytes(code, 0, code.Length, package, 5);
			Array.Copy(data, 0, package, 9, data.Length);
            socket.Send(package);
            packageIdentifier++;
        }
    }
}